﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SchemaMonitor.json
{
    public class CNodeRootList : List<CNodeRoot>
    {
        //Constructors
        private CNodeRootList() : base() { }
        private CNodeRootList(IEnumerable<CNodeRoot> list) : base(list) { }

        public CNodeRootList(int size, CNodeList known, long userId) : base(size)
        {
            _known = known;
            _userId = userId;
        }
        public CNodeRootList(CNodeList known, long userId) : base()
        {
            _known = known;
            _userId = userId;
        }
        public CNodeRootList(CUser u) : this(u.Nodes, u.UserId) { }
        public CNodeRootList(CNodeRootList n) : this(n.Known, n.UserId) { }

        //Members
        private long _userId;
        private CUser _user;
        private CNodeList _known;
        private Dictionary<Guid, CNodeRoot> _index;


        //User Context
        public long UserId { get { return _userId; } }

        public CUser User
        {
            get
            {
                if (null == _user)
                {
                    if (UserId > 0)
                        _user = new CUser(UserId);
                }
                return _user;
            }
        }

        //Internal Cache
        public CNodeList Known
        {
            get
            {
                if (null == _known)
                    _known = new CNodeList();
                return _known;
            }
        }

        //Logging Helper
        public CLog Logging;

        //Indexing
        public List<Guid> Ids
        {
            get
            {
                return new List<Guid>(IndexById.Keys);
            }
        }
        public Dictionary<Guid, CNodeRoot> IndexById
        {
            get
            {
                if (null == _index || _index.Count != this.Count)
                {
                    var temp = new Dictionary<Guid, CNodeRoot>(this.Count);
                    foreach (var i in this)
						if (i.HasId)
                            temp.Add(i.Guid, i);
                    _index = temp;
                }
                return _index;
            }
        }
        public CNodeRoot GetById(Guid id)
        {
            CNodeRoot item = null;
            IndexById.TryGetValue(id, out item);
            return item;
        }

        public CNodeRootList GetByIds(List<Guid> ids)
        {
            var list = new CNodeRootList(ids.Count, _known, _userId);
            foreach (var i in ids)
                list.Add(GetById(i));
            return list;
        }


        //Public
        public CNodeRootList ResolveAdds(CNodeList existing)
        {
            if (0 == existing.Count)
                return this;

            var added = new CNodeRootList(this);
            foreach (var i in this)
                if (!existing.Has(i.Guid))
                    added.Add(i);
            return added;
        }
        public List<Guid> ResolveDeletes(CNodeList existing)
        {
            var deleteIds = new List<Guid>();
            var ids = this.Ids;
            foreach (var i in existing)
                if (!ids.Contains(i.NodeGuid))
                    deleteIds.Add(i.NodeGuid);
            return deleteIds;
        }

        /*
        public CNodeList ResolveChanges(CNodeList existing)
        {
            var changes = new CNodeList();
            foreach (var i in existing)
            {
                var match = GetById(i.NodeGuid);
                if (null == match)
                    continue;

                var oldHash = i.NodeHashSha;
                i.NodeDataAuto = match.Fields.Serialise();    //sets bin, isGz, hash

                if (oldHash != i.NodeHashSha)
                {
                    var diff = new CDiffList(i.Root, match);
                    changes.Add(i);
                }
            }
            return changes;
        }
        */

        public CChangeSet MergeChanges(CNodeList existing, CUser u)   //todo: dict<CNode, CDiffList>
        {
            var changes = new CChangeSet();
            foreach (var i in existing)
            {
                var match = GetById(i.NodeGuid);
                if (null == match)
                    continue;

                var diff = i.Root.Fields.Merge(match.Fields, u);
                if (diff.Count > 0)
                    changes.Add(i, diff);
            }
            return changes;
        }


        public void AddPage(CPage p, CType type, CUser cache)
        {
            //Unexpected?
            if (! p.HasData && null != p.Data && p.Data.Length > 0)
            {
                Add(p, type, cache);
                return;
            }

            //Normal
            AddPage(p.Data, type, cache);
        }
		public void AddPage(List<object> jsonList, CType type, CUser cache)
		{
			AddPage(jsonList.ToArray(), type, cache);
		}
		public void AddPage(object[] jsonArray, CType type, CUser cache)
        {
            foreach (Dictionary<string, object> i in jsonArray)
                this.Add(i, type, cache);
        }
        public void Add(Dictionary<string, object> dict, CType type, CUser cache)
        {
            Add(new CNodeRoot(dict, type, cache));
        }
        public new void Add(CNodeRoot root)
        {
            if (null != _index)
                if (!_index.ContainsKey(root.Guid))
                    _index.Add(root.Guid, root);

            base.Add(root);
        }
    }
}
